#include "myshell.h"
#include <iostream>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>

//把命令行参数表和大小搞成全局变量，1、是因为本来命令行参数对于子进程就是可见的
//2、是因为当子进程进行程序替换时方便，不用传参了，当然不搞成全局变量也可以
#define ARGS 1024
char *gargv[ARGS] = {NULL};
int gargc = 0;

int lastcode = 0;
char pwd[ARGS];//保存当前工作路径

#define NONE_REDIR 0
#define OUTPUT_REDIR 1
#define APPEND_REDIR 2
#define INPUT_REDIR 3
std::string filename;//用来记录重定向的文件
int redir_type = NONE_REDIR;



static std::string GetUserName()
{
    std::string username = getenv("USER");
    return username.empty() ? "None" : username;
}

static std::string GetHostName()
{
    std::string hostname = getenv("HOSTNAME");
    return hostname.empty() ? "None" : hostname;
}

static std::string GetPWD()
{
    //这样写pwd获取的是bash的pwd，但是我们自己写的shell运行时相当于bash的子进程
    //所以我们需要使bash的pwd跟随我们写的shell的pwd变化而变化
    //重点：但是我们写的shell修改了环境变量不会影响bash的pwd，因为子进程（我们自己写的shell）只是复制了一份bash的环境变量，但是如果子进程修改了环境变量，就会发生写时拷贝，此时两者的环境变量已经不同了
    //std::string pwd = getenv("PWD");
    //return pwd;
    
    //我们先把shell的pwd写到bash中
    char temp[1024];
    getcwd(temp, sizeof(temp));//获取当前工作路径
    snprintf(pwd, sizeof(pwd), "PWD=%s",temp);//将temp以"PWD=%s"的形式写入pwd字符串
    //方法不唯一，我们的目的是搞出"PWD=%s"形式的字符串，也可以搞两个string类型然后字符串相+
    putenv(pwd);

    //然后我们截取pwd路径的最后一个位置返回
    std::string temppath = temp;
    size_t pos = temppath.rfind('/');
    if(pos == std::string::npos)
    {
        return "None";
    }
    temppath = temppath.substr(pos+sizeof('/'));
    return temppath.empty() ? "/" : temppath;//有可能最后一个位置就是根目录'/'
}

static std::string GetHomePath()
{
    std::string homepath = getenv("HOME");
    return homepath.empty() ? "/" : homepath;
}

void PrintCommandPrompt()
{
    std::string str1 = GetUserName();
    std::string str2 = GetHostName();
    std::string str3 = GetPWD();
    printf("[%s@%s %s]$ ", str1.c_str(), str2.c_str(), str3.c_str());
}

bool GetCommandString (char commandstr[], int len)
{
    if(commandstr == NULL || len <= 0)
        return false;
    char* res = fgets(commandstr, len, stdin);
    if(res == NULL)
        return false;
    //你输入一串字符串时一定会在最后打入回车，所以\n也会被输入进去，所以要处理一下
    commandstr[strlen(commandstr) - 1] = 0;
    return strlen(commandstr) == 0 ? false : true;
}


//找到重定向符号后面的文件名 ，防止cat<       log.txt这种情况的出现
#define TrimSpace(start) do{\
    while(isspace(*start))\
    {\
        start++;\
    }\
}while(0)//不用担心重定向符后一堆空格没有文件名的情况，因为上面的获取字符将最后一个字符改为了\0
//也可以写成#define TrimSpace(start) while(isspace(*start)) start++;

void CheckRedir(char cmd[])
{
    char* start = cmd;
    char* end = cmd + strlen(cmd) - 1;

    while(start <= end)
    {
        if(*start == '>')
        {
            if(*(start + 1) == '>')//追加重定向
            {
                redir_type = APPEND_REDIR;
                *start = '\0';
                start += 2;
                TrimSpace(start);
                filename = start;
                break;
            }
            else//输出重定向
            {
                redir_type = OUTPUT_REDIR;
                *start = '\0';
                start++;
                TrimSpace(start);
                filename = start;
                break;
            }
        }
        else if(*start == '<')//输入重定向
        {
            redir_type = INPUT_REDIR;
            *start = '\0';
            start++;
            TrimSpace(start);
            filename = start;
            break;
        }
        else
        {
            start++;
        }
    }
}

bool ParseCommandString(char cmd[])
{
    if(gargv == NULL || cmd == NULL)
        return false;
#define SEP " "
    gargv[gargc++] = strtok(cmd, SEP);
    while(gargv[gargc++] = strtok(NULL, SEP));
    gargc--;//最后一次执行while命令虽然失败了，但是gargc仍然++了
//#define DEBUG
#ifdef DEBUG
    printf("%d\n", gargc);
    printf("----------------\n");
    for(int i = 0; i < gargc;i++)
    {
        printf("gargv[%d]:%s ", gargc, gargv[i]);
    }
    printf("\n");
    printf("----------------\n");
#endif
    return true;
}
void InitGlobal()
{
    gargc = 0;
    memset(gargv, 0, sizeof(gargv));
    redir_type = NONE_REDIR;
    filename.clear();
}

void ForkAndExec()
{
    pid_t id = fork();
    if(id < 0)
    {
        perror("fork");
        return;
    }
    else if(id == 0)
    {
        //重定向和程序替换互不影响，先打开该打开的文件，在进行程序替换
        if(redir_type == OUTPUT_REDIR)
        {
            int outputfd = open(filename.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
            dup2(outputfd, 1);
        }
        else if(redir_type == APPEND_REDIR)
        {
            int appendfd = open(filename.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);
            dup2(appendfd, 1);
        }
        else if(redir_type == INPUT_REDIR)
        {
            int inputfd = open(filename.c_str(), O_RDONLY);
            dup2(inputfd, 0);
        }
        else
        {
            //不需要重定向，不进行操作
        }
        execvp(gargv[0], gargv);
        exit(0);
    }
    else
    {
        int status = 0;
        pid_t rid = waitpid(id, &status, 0);
        if(rid > 0)
            lastcode = WEXITSTATUS(status);
    }
}

bool BuildInCommand()
{
    std::string cmd = gargv[0];
    bool ret = false;
    if(cmd == "cd")
    {
        if(gargc == 2)
        {
            std::string target = gargv[1];
            if(target == "~")
            {
                ret = true;
                lastcode = 0;
                chdir(GetHomePath().c_str());
            }
            else 
            {
                ret = true;
                lastcode = 0;
                chdir(target.c_str());
            }
        }
        else if(gargc == 1)
        {
            ret = true;
            lastcode = 0;
            chdir(GetHomePath().c_str());
        }
    }
    else if(cmd == "echo")
    {
        if(gargc == 2)
        {
            std::string args = gargv[1];
            if(args[0] == '$')
            {
                if(args[1] == '?')
                {
                    printf("lastcode: %d\n",lastcode);
                    lastcode = 0;
                    ret = true;
                }
                else//$+变量（本地变量或环境变量）= 拿到该变量的值（这里只写了环境变量） 
                {
                    const char* name = &args[1];
                    printf("%s\n",getenv(name));
                    ret = true;
                    lastcode = 0;
                }
            }
            else 
            {
                printf("%s\n",args.c_str());
                lastcode = 0;
                ret = true;
            }
        }
    }
    return ret;
}
